/**
 * @file test_buffer.cpp
 * @author 逆流 (1171267147@qq.com)
 * @brief 测试OpenCL内存对象和缓冲区操作功能
 * @version 0.3
 * @date 2025-11-29
 *
 * @copyright Copyright (c) 2025
 * @note 此文件由 iFlow CLI 生成和维护
 *
 */
#define BOOST_TEST_MODULE TestBuffer
#include <CL/cl.h>

#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/format/format_fwd.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/smart_ptr/scoped_array.hpp>
#include <boost/test/included/unit_test.hpp>
#include <boost/test/tools/old/interface.hpp>
#include <boost/test/unit_test_log.hpp>
#include <boost/timer/timer.hpp>
#include <cstddef>
#include <cstring>
#include <fstream>
#include <iostream>
#include <vector>

#include "test_fixture.hpp"
#include "utility.hpp"

using boost::format;

class BufferTestFixture : public CLTestFixture {
protected:
    BufferTestFixture() : CLTestFixture() {}
};

bool VerifyData(const int* origin, const int* result, size_t length)
{
    auto log_to_file = [&]()
    {
        std::ofstream log("error_data.txt", std::ios::trunc | std::ios::out);
        for (size_t i = 0; i < length; i++)
        {
            log << format("%4d\t%4d\n") % origin[i] % result[i];
        }
        log.close();
    };

    bool is_same = true;
    for (size_t i = 0; i < length; i++)
    {
        if (origin[i] != result[i])
        {
            std::cerr << format("Error at index %d: %4d != %4d\n") % i %
                             origin[i] % result[i];
            is_same = false;
            log_to_file();
            break;
        }
    }
    return is_same;
}

bool VerifyDataFloat(const float* origin, const float* result, size_t length)
{
    bool is_same = true;
    for (size_t i = 0; i < length; i++)
    {
        if (std::abs(origin[i] - result[i]) > 1e-6)
        {
            std::cerr << format("Error at index %d: %f != %f\n") % i %
                             origin[i] % result[i];
            is_same = false;
            break;
        }
    }
    return is_same;
}

BOOST_FIXTURE_TEST_CASE(BasicWriteAndRead, BufferTestFixture)
{
    size_t num_elements = 1024 * 1024;

    cl_mem buffer = clCreateBuffer(
        context_, CL_MEM_READ_WRITE, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(buffer);

    boost::scoped_array<int> data(new int[num_elements]);

    GeneratorRandom(data.get(), num_elements);

    boost::timer::cpu_timer t;

    cl_int err = clEnqueueWriteBuffer(queue_,
                                      buffer,
                                      CL_TRUE,
                                      0,
                                      num_elements * sizeof(int),
                                      data.get(),
                                      0,
                                      NULL,
                                      NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_TEST_MESSAGE("write buffer: " << t.format());

    boost::scoped_array<int> result(new int[num_elements]);

    t.start();
    err = clEnqueueReadBuffer(queue_,
                              buffer,
                              CL_TRUE,
                              0,
                              num_elements * sizeof(int),
                              result.get(),
                              0,
                              NULL,
                              NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_TEST_MESSAGE("read buffer: " << t.format());

    BOOST_CHECK(VerifyData(data.get(), result.get(), num_elements));

    clReleaseMemObject(buffer);
}

BOOST_FIXTURE_TEST_CASE(WriteAndReadDifferentTypes, BufferTestFixture)
{
    size_t num_elements = 1024;

    cl_mem buffer = clCreateBuffer(
        context_, CL_MEM_READ_WRITE, sizeof(float) * num_elements, NULL, NULL);
    BOOST_CHECK(buffer);

    boost::scoped_array<float> data(new float[num_elements]);
    for (size_t i = 0; i < num_elements; i++)
    {
        data[i] = static_cast<float>(i) * 0.5f;
    }

    cl_int err = clEnqueueWriteBuffer(queue_,
                                      buffer,
                                      CL_TRUE,
                                      0,
                                      num_elements * sizeof(float),
                                      data.get(),
                                      0,
                                      NULL,
                                      NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    boost::scoped_array<float> result(new float[num_elements]);

    err = clEnqueueReadBuffer(queue_,
                              buffer,
                              CL_TRUE,
                              0,
                              num_elements * sizeof(float),
                              result.get(),
                              0,
                              NULL,
                              NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    BOOST_CHECK(VerifyDataFloat(data.get(), result.get(), num_elements));

    clReleaseMemObject(buffer);
}

BOOST_FIXTURE_TEST_CASE(WriteRectAndReadRect, BufferTestFixture)
{
    size_t num_elements = 1024 * 1024;

    cl_mem buffer = clCreateBuffer(
        context_, CL_MEM_READ_WRITE, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(buffer);

    boost::scoped_array<int> data(new int[1024]);
    GeneratorRandom(data.get(), 1024);

    size_t buffer_origin[3]   = {100 * sizeof(int), 0, 0};
    size_t host_origin[3]     = {0, 0, 0};
    size_t region[3]          = {1024 * sizeof(int), 1, 1};
    size_t buffer_row_pitch   = 0;
    size_t buffer_slice_pitch = 0;
    size_t host_row_pitch     = 0;
    size_t host_slice_pitch   = 0;

    boost::timer::cpu_timer t;
    cl_int                  err = clEnqueueWriteBufferRect(queue_,
                                          buffer,
                                          CL_TRUE,
                                          buffer_origin,
                                          host_origin,
                                          region,
                                          buffer_row_pitch,
                                          buffer_slice_pitch,
                                          host_row_pitch,
                                          host_slice_pitch,
                                          data.get(),
                                          0,
                                          NULL,
                                          NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    std::cout << format("write rect: %s ns\n") % t.elapsed().wall;

    boost::scoped_array<int> result(new int[1024]);

    t.start();
    err = clEnqueueReadBufferRect(queue_,
                                  buffer,
                                  CL_TRUE,
                                  buffer_origin,
                                  host_origin,
                                  region,
                                  buffer_row_pitch,
                                  buffer_slice_pitch,
                                  host_row_pitch,
                                  host_slice_pitch,
                                  result.get(),
                                  0,
                                  NULL,
                                  NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    std::cout << format("read rect: %s ns\n") % t.elapsed().wall;

    BOOST_CHECK(VerifyData(data.get(), result.get(), 1024));

    clReleaseMemObject(buffer);
}

BOOST_FIXTURE_TEST_CASE(BufferMapUnmap, BufferTestFixture)
{
    size_t num_elements = 1024;

    cl_mem buffer = clCreateBuffer(
        context_, CL_MEM_READ_WRITE, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(buffer);

    boost::scoped_array<int> data(new int[num_elements]);
    GeneratorRandom(data.get(), num_elements);

    // 写入初始数据
    cl_int err = clEnqueueWriteBuffer(queue_,
                                      buffer,
                                      CL_TRUE,
                                      0,
                                      num_elements * sizeof(int),
                                      data.get(),
                                      0,
                                      NULL,
                                      NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 映射缓冲区到主机内存
    cl_int map_err = CL_SUCCESS;
    int*   mapped_ptr =
        static_cast<int*>(clEnqueueMapBuffer(queue_,
                                             buffer,
                                             CL_TRUE,
                                             CL_MAP_READ | CL_MAP_WRITE,
                                             0,
                                             num_elements * sizeof(int),
                                             0,
                                             NULL,
                                             NULL,
                                             &map_err));
    BOOST_CHECK_EQUAL(map_err, CL_SUCCESS);
    BOOST_CHECK(mapped_ptr);

    // 在映射的内存中进行修改
    for (size_t i = 0; i < num_elements; ++i)
    {
        mapped_ptr[i] *= 2;  // 将值翻倍
    }

    // 取消映射
    err = clEnqueueUnmapMemObject(queue_, buffer, mapped_ptr, 0, NULL, NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 读取数据以验证修改
    boost::scoped_array<int> result(new int[num_elements]);
    err = clEnqueueReadBuffer(queue_,
                              buffer,
                              CL_TRUE,
                              0,
                              num_elements * sizeof(int),
                              result.get(),
                              0,
                              NULL,
                              NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 验证数据是否正确翻倍
    bool is_correct = true;
    for (size_t i = 0; i < num_elements; ++i)
    {
        if (result[i] != data[i] * 2)
        {
            is_correct = false;
            break;
        }
    }
    BOOST_CHECK(is_correct);

    clReleaseMemObject(buffer);
}

BOOST_FIXTURE_TEST_CASE(BufferSubBuffer, BufferTestFixture)
{
    size_t num_elements = 1024;
    size_t sub_elements = 256;

    cl_mem parent_buffer = clCreateBuffer(
        context_, CL_MEM_READ_WRITE, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(parent_buffer);

    // 创建子缓冲区
    cl_buffer_region region;
    region.origin = 256 * sizeof(int);           // 从第256个元素开始
    region.size   = sub_elements * sizeof(int);  // 256个元素的大小

    cl_mem sub_buffer = clCreateSubBuffer(parent_buffer,
                                          CL_MEM_READ_WRITE,
                                          CL_BUFFER_CREATE_TYPE_REGION,
                                          &region,
                                          NULL);
    BOOST_CHECK(sub_buffer);

    // 为子缓冲区准备数据
    boost::scoped_array<int> sub_data(new int[sub_elements]);
    for (size_t i = 0; i < sub_elements; ++i)
    {
        sub_data[i] = static_cast<int>(i + 1000);
    }

    // 写入子缓冲区
    cl_int err = clEnqueueWriteBuffer(queue_,
                                      sub_buffer,
                                      CL_TRUE,
                                      0,
                                      sub_elements * sizeof(int),
                                      sub_data.get(),
                                      0,
                                      NULL,
                                      NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 读取整个父缓冲区
    boost::scoped_array<int> full_result(new int[num_elements]);
    err = clEnqueueReadBuffer(queue_,
                              parent_buffer,
                              CL_TRUE,
                              0,
                              num_elements * sizeof(int),
                              full_result.get(),
                              0,
                              NULL,
                              NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 验证子缓冲区对应位置的数据是否正确
    bool is_correct = true;
    for (size_t i = 0; i < sub_elements; ++i)
    {
        if (full_result[256 + i] != sub_data[i])
        {
            is_correct = false;
            break;
        }
    }
    BOOST_CHECK(is_correct);

    clReleaseMemObject(sub_buffer);
    clReleaseMemObject(parent_buffer);
}

BOOST_FIXTURE_TEST_CASE(BufferCopy, BufferTestFixture)
{
    size_t num_elements = 1024;

    cl_mem src_buffer = clCreateBuffer(
        context_, CL_MEM_READ_ONLY, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(src_buffer);

    cl_mem dst_buffer = clCreateBuffer(
        context_, CL_MEM_WRITE_ONLY, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(dst_buffer);

    // 准备源数据
    boost::scoped_array<int> src_data(new int[num_elements]);
    for (size_t i = 0; i < num_elements; ++i)
    {
        src_data[i] = static_cast<int>(i + 1);
    }

    // 写入源缓冲区
    cl_int err = clEnqueueWriteBuffer(queue_,
                                      src_buffer,
                                      CL_TRUE,
                                      0,
                                      num_elements * sizeof(int),
                                      src_data.get(),
                                      0,
                                      NULL,
                                      NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 执行缓冲区复制
    err = clEnqueueCopyBuffer(queue_,
                              src_buffer,
                              dst_buffer,
                              0,
                              0,
                              num_elements * sizeof(int),
                              0,
                              NULL,
                              NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    clFinish(queue_);

    // 读取目标缓冲区
    boost::scoped_array<int> dst_result(new int[num_elements]);
    err = clEnqueueReadBuffer(queue_,
                              dst_buffer,
                              CL_TRUE,
                              0,
                              num_elements * sizeof(int),
                              dst_result.get(),
                              0,
                              NULL,
                              NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 验证复制是否成功
    BOOST_CHECK(VerifyData(src_data.get(), dst_result.get(), num_elements));

    clReleaseMemObject(src_buffer);
    clReleaseMemObject(dst_buffer);
}

BOOST_FIXTURE_TEST_CASE(BufferWithDifferentFlags, BufferTestFixture)
{
    size_t num_elements = 512;

    // 测试只读缓冲区
    cl_mem read_only_buffer = clCreateBuffer(
        context_, CL_MEM_READ_ONLY, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(read_only_buffer);

    // 测试只写缓冲区
    cl_mem write_only_buffer = clCreateBuffer(
        context_, CL_MEM_WRITE_ONLY, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(write_only_buffer);

    // 测试可读写缓冲区
    cl_mem read_write_buffer = clCreateBuffer(
        context_, CL_MEM_READ_WRITE, sizeof(int) * num_elements, NULL, NULL);
    BOOST_CHECK(read_write_buffer);

    // 测试使用主机内存作为初始数据的缓冲区
    boost::scoped_array<int> host_data(new int[num_elements]);
    for (size_t i = 0; i < num_elements; ++i)
    {
        host_data[i] = static_cast<int>(i * 3);
    }

    cl_mem buffer_with_host_ptr =
        clCreateBuffer(context_,
                       CL_MEM_USE_HOST_PTR | CL_MEM_READ_WRITE,
                       sizeof(int) * num_elements,
                       host_data.get(),
                       NULL);
    BOOST_CHECK(buffer_with_host_ptr);

    // 验证使用主机指针的缓冲区
    boost::scoped_array<int> result(new int[num_elements]);
    cl_int                   err = clEnqueueReadBuffer(queue_,
                                     buffer_with_host_ptr,
                                     CL_TRUE,
                                     0,
                                     num_elements * sizeof(int),
                                     result.get(),
                                     0,
                                     NULL,
                                     NULL);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    BOOST_CHECK(VerifyData(host_data.get(), result.get(), num_elements));

    clReleaseMemObject(read_only_buffer);
    clReleaseMemObject(write_only_buffer);
    clReleaseMemObject(read_write_buffer);
    clReleaseMemObject(buffer_with_host_ptr);
}

// 测试内存对象信息查询
BOOST_AUTO_TEST_CASE(TestMemoryObjectInfo)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    const size_t buffer_size = 256 * sizeof(int);
    cl_int       err         = CL_SUCCESS;
    cl_mem       buffer =
        clCreateBuffer(context, CL_MEM_READ_WRITE, buffer_size, nullptr, &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(buffer != nullptr);

    // 测试查询内存对象信息
    cl_context buffer_context = nullptr;
    err                       = clGetMemObjectInfo(buffer,
                             CL_MEM_CONTEXT,
                             sizeof(buffer_context),
                             &buffer_context,
                             nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(buffer_context == context);

    cl_mem_flags flags = 0;
    err                = clGetMemObjectInfo(
        buffer, CL_MEM_FLAGS, sizeof(flags), &flags, nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    // 检查至少包含读写标志
    BOOST_CHECK((flags & CL_MEM_READ_WRITE) == CL_MEM_READ_WRITE);

    size_t size = 0;
    err = clGetMemObjectInfo(buffer, CL_MEM_SIZE, sizeof(size), &size, nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_EQUAL(size, buffer_size);

    cl_uint map_count = 0;
    err               = clGetMemObjectInfo(
        buffer, CL_MEM_MAP_COUNT, sizeof(map_count), &map_count, nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_EQUAL(map_count, 0);  // 初始时应该没有映射

    cl_uint ref_count = 0;
    err               = clGetMemObjectInfo(
        buffer, CL_MEM_REFERENCE_COUNT, sizeof(ref_count), &ref_count, nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_GT(ref_count, 0);

    // 清理
    err = clReleaseMemObject(buffer);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    cl.DeInit();
}

// 测试内存对象的引用计数
BOOST_AUTO_TEST_CASE(TestMemoryObjectReferenceCounting)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    const size_t buffer_size = 128 * sizeof(int);
    cl_int       err         = CL_SUCCESS;
    cl_mem       buffer =
        clCreateBuffer(context, CL_MEM_READ_WRITE, buffer_size, nullptr, &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(buffer != nullptr);

    // 获取初始引用计数
    cl_uint initial_ref_count = 0;
    err                       = clGetMemObjectInfo(buffer,
                             CL_MEM_REFERENCE_COUNT,
                             sizeof(initial_ref_count),
                             &initial_ref_count,
                             nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_GT(initial_ref_count, 0);

    // 增加引用计数
    err = clRetainMemObject(buffer);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 检查引用计数是否增加
    cl_uint ref_count_after_retain = 0;
    err                            = clGetMemObjectInfo(buffer,
                             CL_MEM_REFERENCE_COUNT,
                             sizeof(ref_count_after_retain),
                             &ref_count_after_retain,
                             nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_EQUAL(ref_count_after_retain, initial_ref_count + 1);

    // 释放一次，引用计数应减少
    err = clReleaseMemObject(buffer);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 检查引用计数是否减少
    cl_uint ref_count_after_release = 0;
    err                             = clGetMemObjectInfo(buffer,
                             CL_MEM_REFERENCE_COUNT,
                             sizeof(ref_count_after_release),
                             &ref_count_after_release,
                             nullptr);
    if (err == CL_SUCCESS)
    {  // 某些实现可能在对象被完全释放后无法查询信息
        BOOST_CHECK_EQUAL(ref_count_after_release, initial_ref_count);
    }

    // 再次释放，这应该真正释放对象
    err = clReleaseMemObject(buffer);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    cl.DeInit();
}

// 测试异步写入和读取操作
BOOST_FIXTURE_TEST_CASE(AsyncWriteAndRead, BufferTestFixture)
{
    size_t num_elements = 512;
    size_t buffer_size  = num_elements * sizeof(float);

    cl_mem buffer =
        clCreateBuffer(context_, CL_MEM_READ_WRITE, buffer_size, NULL, NULL);
    BOOST_CHECK(buffer);

    // 准备主机数据
    boost::scoped_array<float> host_data(new float[num_elements]);
    for (size_t i = 0; i < num_elements; i++)
    {
        host_data[i] = static_cast<float>(i) * 0.5f;
    }

    // 异步写入缓冲区（非阻塞）
    cl_event write_event = NULL;
    cl_int   err         = clEnqueueWriteBuffer(queue_,
                                      buffer,
                                      CL_FALSE,  // 非阻塞写入
                                      0,
                                      buffer_size,
                                      host_data.get(),
                                      0,
                                      nullptr,
                                      &write_event);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(write_event != NULL);

    // 异步读取缓冲区（非阻塞）
    boost::scoped_array<float> read_data(new float[num_elements]);
    cl_event                   read_event = NULL;
    err                                   = clEnqueueReadBuffer(queue_,
                              buffer,
                              CL_FALSE,  // 非阻塞读取
                              0,
                              buffer_size,
                              read_data.get(),
                              1,  // 等待写入事件完成
                              &write_event,
                              &read_event);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(read_event != NULL);

    // 等待读取完成
    err = clWaitForEvents(1, &read_event);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 验证数据
    bool data_matches = true;
    for (size_t i = 0; i < num_elements; i++)
    {
        if (std::abs(host_data[i] - read_data[i]) > 1e-6)
        {
            data_matches = false;
            break;
        }
    }
    BOOST_CHECK(data_matches);

    // 清理事件
    err = clReleaseEvent(write_event);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    err = clReleaseEvent(read_event);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 清理缓冲区
    clReleaseMemObject(buffer);
}